﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Runtime.InteropServices;

using System.Diagnostics;
using System.Drawing;

using System.Threading;
using System.Threading.Tasks;

using Styx;
using Styx.Common;
using Styx.Common.Helpers;

using Styx.CommonBot;
using Styx.CommonBot.POI;
using Styx.CommonBot.Routines;
using Styx.CommonBot.Coroutines;

using Styx.Helpers;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.DBC;
using Styx.WoWInternals.WoWObjects;

using CommonBehaviors.Actions;
using Action = Styx.TreeSharp.Action;

using KittyDru.Helpers;
using System.Reflection;

namespace KittyDru.Rotation
{
    internal static class Rotation {
        #region Rotation Chooser

        public enum RotationType
        {
            AOE,
            Singilar,
            NULL
        }

        public static void doSet(RotationType r ){
            lastRotationType = r;
        }

        public static RotationType doGet()
        {
            return lastRotationType;
        }

        private static RotationType lastRotationType { get; set; }
    }

    class PVE
    {

        #region Basic Variables
        private static LocalPlayer Me { get { return Styx.StyxWoW.Me; } }
        private static WoWUnit MyTarget { get { return Me.GotTarget ? Me.CurrentTarget : null; } }

        private static bool WeakAura_allowed = false;
        private static WoWUnit _lastTarget = null;

        private static int ComboPoints()
        {
            return Lua.GetReturnVal<int>("return GetComboPoints(\"player\")", 0);
        }

        #endregion

        #region Spell Checks - Do we need to cast ?
        public static bool canUseShred()
        {
            return true;
            //if (Me.IsBehind(MyTarget) || (Character.Glyphs[Spells.Shred] == true && (Me.HasAura(Spells.TigersFury) || Me.HasAura(Spells.Berserk)))
            //) return true;
            //return false;
        }
        private static bool HeroismOn()
        {
            foreach (string Spell in Spells.HeroismSpells)
            {
                if (Me.HasAura(Spell)) return true;
            }
            return false;
        }
        #endregion

        #region Cast Spells
        private static bool AutoAttack()
        {
            if (!Me.IsAutoAttacking)
            {
                Utilities.log("Autoatack");
                Lua.DoString("StartAttack()");
                return true;
            }
            return false;
        }
        public static int Thrash()
        {
            // Взбучка
            if (MyTarget.HasMyAura(Spells.Thrash)) return 0;

            if (global::KittyDru.Rotation.Rotation.doGet() == global::KittyDru.Rotation.Rotation.RotationType.AOE) return 100;
            else if (
                (Unit.UnfriendlyUnits(7).Count() > 2 && !MyTarget.HasMyAura(Spells.Thrash)) ||
                (Unit.UnfriendlyUnits(7).Count() > 1 && !MyTarget.HasMyAura(Spells.Thrash) && MyTarget.HasMyAura(Spells.Rake)) ||
                (MyTarget.HasMyAura(Spells.Rake) /*&& MyTarget.HasMyAura(Spells.Rip)*/ && !MyTarget.HasMyAura(Spells.Thrash))
                )
            {
                return 50;
            }
            return 0;
        }

        public static int Swipe()
        {
            // Размах
            return 10;
        }
        /*private static async Task<bool> FaerieFire()
        {
            // Волшебный огонь
            if (MyTarget.Distance <= 35 && 
                (!MyTarget.HasAura(Spells.FaerieFire) || (MyTarget.HasAura(Spells.FaerieFire) && !MyTarget.HasAura(Spells.WeakenedArmor) && WeakAura_allowed))
               )
            {
                if (await Spells.SpellCast(Spells.FaerieFire))
                {
                    WeakAura_allowed = false;
                    if (MyTarget != null && MyTarget.IsAlive && MyTarget.IsValid)
                    {
                        if (MyTarget.HasAura(Spells.WeakenedArmor)) WeakAura_allowed = true;
                    }
                    return true;
                }
            }
            return false;
        }*/
        private static async Task<bool> CatForm()
        {
            if (!Me.HasAura(Spells.FormCat)) return await Spells.BuffSelf(Spells.FormCat);
            return false;
        }
        public static int Rip()
        {
            // Разорвать
            if (ComboPoints() == 5 &&
                        (!MyTarget.HasMyAura(Spells.Rip) || //MyTarget.MyAuras()[Spells.Rip].TimeLeft.Seconds < 2 ||
                            (Character.DotsTF[Spells.Rip] == false && Me.HasAura(Spells.TigersFury))
                        )
                )
                return 75;
            return 0;
        }
        public static int Rake()
        {

            // Глубокая рана
            if (
                !MyTarget.HasMyAura(Spells.Rake) || 
               // MyTarget.MyAuras()[Spells.Rake].TimeLeft.Seconds < 2 || 
                (Character.DotsTF[Spells.Rake] == false && Me.HasAura(Spells.TigersFury) && MyTarget.HasMyAura(Spells.Rip))
               )
                return 60;
            
            return 0;
        }
        public static int FerociousBite()
        {
            // Свирепый укус
            if (
                    MyTarget.HasMyAura(Spells.Rip)  &&
                (
                    // Не Экзекут фаза
                    (
                        ComboPoints() == 5 && MyTarget.MyAuras()[Spells.Rip].TimeLeft.Seconds > 14 &&
                        (
                          Me.CurrentEnergy > 50 ||
                          Me.HasAura(Spells.Clearcasting)
                        ) 
                    )
                    ||
                    // Экзекут фаза
                    MyTarget.HealthPercent <= 25 && (ComboPoints() == 5 || MyTarget.MyAuras()[Spells.Rip].TimeLeft.Seconds < 3)
                )
               )
            {
                return 100;
            }

            return 0;
        }
        public static int Shred()
        {
            if(!MyTarget.HasMyAura(Spells.Rake) &&
                (
                    !MyTarget.HasMyAura(Spells.Thrash) && 
                    (
                        ComboPoints() == 5 || 
                        Me.HasAura(Spells.Clearcasting) || 
                        (Me.HasAura(Spells.Berserk) && (Me.CurrentEnergy - Math.Ceiling((double)Spells.EnergyCost[Spells.Thrash]/2)) > 0) 
                    )
               )
            )
                return 0;
            return 1;

            // Увечье \ Полоснуть
           /* if (
                MyTarget.HasMyAura(Spells.Rake) && (
                    // Нужен комбогенератор
                    (!MyTarget.HasMyAura(Spells.Rip) && ComboPoints() < 5) ||
                    //Комбогенератор не нужен
                    (
                     ((Character.DotHaveEnothTime(Spells.Rake, Spells.Shred) || Character.HaveMoreEnergy(Spells.Rake, Spells.Shred))) &&
                     ((MyTarget.HasMyAura(Spells.Rip) && (Character.DotHaveEnothTime(Spells.Rip, Spells.Shred) || Character.HaveMoreEnergy(Spells.Rake, Spells.Shred))) || ComboPoints() < 5) &&
                     (MyTarget.HasMyAura(Spells.Thrash) || Me.CurrentEnergy < 65)
                    )
                )

            )
            {
                if (canUseShred() &&
                    (
                        ComboPoints() > 3 ||
                        (MyTarget.HasMyAura(Spells.Rip) && MyTarget.HasMyAura(Spells.Thrash) && MyTarget.MyAuras()[Spells.Rip].TimeLeft.Seconds > 12)
                    )
                )
                return await Spells.SpellCast(Spells.Shred);
                //if (await Spells.SpellCast(Spells.Mangle)) return true;
            }
            return false;*/
        }
        #endregion 

        #region Cast Procs Spells
        public static async Task<bool> PredatorySwiftness(){
            if (Me.HasAura(Spells.PredatorySwiftness) && Me.HealthPercent < 70) return await Spells.SpellCast(Spells.HealingTouch);
            return false;
        }
        #endregion

        #region Cast CD Spells
        private static async Task<bool> SavageRoar()
        {
            // Дикий рев
            if (
                (
                    !Me.HasAura(Spells.SavageRoar) || 
                    (MyTarget.HasMyAura(Spells.SavageRoar) && MyTarget.MyAuras()[Spells.Rip].TimeLeft.Seconds < 2)
                ) && 
                (ComboPoints() == 5 || !MyTarget.HasMyAura(Spells.Rip))
               )
            {
                if (await Spells.BuffSelf(Spells.SavageRoar)) return true;
            }
            return false;
        }
        private static async Task<bool> TigersFury()
        {
            // Тигриное неистовство
            if (!Me.HasAura(Spells.TigersFury) && Me.CurrentEnergy < 35)
            {
                if (await Spells.BuffSelf(Spells.TigersFury)) return true;
            }
            return false;
        }
        private static async Task<bool> Berserk()
        {
            // Берсерк
            if (!Me.HasAura(Spells.Berserk) && !HeroismOn() /*&& Spells.SpellCDLeft(Spells.TigersFury) >= 10*/)
            {
                if (MyTarget.IsBoss || MyTarget.Elite || Unit.UnfriendlyUnits(8).Where(u => u.MaxHealth > 300000).Count() > 7 || Unit.IsTrainingDummy(MyTarget))
                {
                    if (await Spells.SpellCast(Spells.Berserk)) return true;
                }
            }
            return false;
        }


        private static async Task<bool> IncarnationKingoftheJungle()
        {
            // IncarnationKingoftheJungle
            if (!Spells.CDs.Contains(Spells.IncarnationKingoftheJungle))
            {
                if (MyTarget.IsBoss || MyTarget.Elite || Unit.UnfriendlyUnits(8).Where(u => u.MaxHealth > 300000).Count() > 7 || Unit.IsTrainingDummy(MyTarget))
                {
                    if (await Spells.SpellCast(Spells.IncarnationKingoftheJungle)) return true;
                }
            }
            return false;
        }
        #endregion

        #region Interrupt
        private static bool NeedInterrupt() 
        {
            return (MyTarget.IsCasting || MyTarget.IsChanneling) && MyTarget.CanInterruptCurrentSpellCast;
        }

        private static async Task<bool> Interrupter()
        {
            // Прерывание
            if (NeedInterrupt())
            {
                string spellName = MyTarget.IsCasting ? MyTarget.CastingSpell.Name : MyTarget.ChanneledSpell.Name;

                bool interrupted = false;

                if (await Spells.CastMeleeSpell(Spells.SkullBash, spellName)) interrupted = true;
                else if (await Spells.CastMeleeSpell(Spells.MightyBash, spellName)) interrupted = true;
                // Радиус Тайфуна 30 метров
                else if (await Spells.CastRangeSpell(Spells.Typhoon, 30)) interrupted = true;

                if (interrupted == true)
                {
                    Styx.StyxWoW.Sleep(500);
                    return true;
                }
            }
            return false;
        }
        #endregion

        #region Target Checks
        private static void CheckTarget()
        {
            if (_lastTarget != MyTarget)
            {
                WeakAura_allowed = false;
                Spells.LastCast = null;
                _lastTarget = MyTarget;
            }
        }
        #endregion



        public static async Task<bool> RotationsChooser()
        {
            if (Utilities.BusyCheck()) return true;
            if (await Interrupter()) return true;

            //|| MyTarget.IsFriendly || !MyTarget.IsHostile
            if (MyTarget == null || !MyTarget.Attackable) return true;

            // Проверка изменилась ли цель
            CheckTarget();

            // Форма кота
            if (await CatForm()) return true;


            // Мы не в мили радиусе от цели
            if (!MyTarget.IsWithinMeleeRange)
            {
                Utilities.log("Not in melee range");
                return true;
            }

            // Автоатака
            AutoAttack();

            //Utilities.log("Combopoints: " + ComboPoints().ToString());

            // Кулдауны
            if (await CDRotation()) return true;
            if (await PredatorySwiftness()) return true;

            //Utilities.log("Rotation: " + global::KittyDru.Rotation.Rotation.doGet().ToString());

            if(Unit.UnfriendlyUnits(8).Count() > 2) {
                global::KittyDru.Rotation.Rotation.doSet(global::KittyDru.Rotation.Rotation.RotationType.AOE);
                return await AoeRotation();
            } else {
                global::KittyDru.Rotation.Rotation.doSet(global::KittyDru.Rotation.Rotation.RotationType.Singilar);
                return await SingleRotation();
            }

        }
        #endregion



        #region Rotations 


        #region CD Rotation
        private static async Task<bool> CDRotation()
        {

            // Дикий рев
            if (await SavageRoar()) return true;

            // Тигриное неистовство
            if (await TigersFury()) return true;

            if (await IncarnationKingoftheJungle()) return true;

            // Берсерк
            if (await Berserk()) return true;

            return false;
        }
        #endregion

        #region AOE Rotation
        public static async Task<bool> AoeRotation()
        {

           // Utilities.log("0 Starting AOE Rotation, units in 8 meters : " + Unit.UnfriendlyUnits(8).Count().ToString());

            if (global::KittyDru.Rotation.Rotation.doGet() != global::KittyDru.Rotation.Rotation.RotationType.AOE) Utilities.log("Starting AOE Rotation, units in 8 meters : " + Unit.UnfriendlyUnits(8).Count().ToString());

            return await Spells.SpellCast(getSpellsPriority("SpellPriorityAOE"));
        }
        #endregion

        #region SingleTarget Rotation
        public static async Task<bool> SingleRotation()
        {
            //Utilities.log("0 Starting Single Rotation, units in 8 meters : " + Unit.UnfriendlyUnits(8).Count().ToString());

            if (global::KittyDru.Rotation.Rotation.doGet() != global::KittyDru.Rotation.Rotation.RotationType.Singilar) Utilities.log("Starting Single Rotation, units in 8 meters : " + Unit.UnfriendlyUnits(8).Count().ToString());

            return await Spells.SpellCast(getSpellsPriority("SpellPrioritySingle"));
        }


        private static Dictionary<string, int> SpellPrioritySingle = new Dictionary<string, int>()
        {
            {Spells.FerociousBite,      0},
            {Spells.Rip,                0},
            {Spells.Rake,               0},
            {Spells.Thrash,             0},
            {Spells.Shred,              1}

        };

        private static Dictionary<string, int> SpellPriorityAOE = new Dictionary<string, int>()
        {
            {Spells.Thrash,               0},
            {Spells.Swipe,                1},
        };


        private static string getSpellsPriority(string rotation) {


            Type type = typeof(PVE);
            Type dictType = typeof(Dictionary<string, int>);



            //string spellName = (from entry in (Dictionary<string, int>)(rotation == "SpellPriorityAOE" ? SpellPriorityAOE : SpellPrioritySingle) 
             //   orderby entry.Value descending select entry.Key).ToString();

            string spellName = null ;//= (rotation == "SpellPriorityAOE" ? SpellPriorityAOE : SpellPrioritySingle).Aggregate((l, r) => l.Value > r.Value ? l : r).Key;
                //Aggregate((l, r) => l.Value > r.Value ? l : r).Key;

           // Utilities.log("Spell to use: " + spellName);

            int spellPrior = 0;
            int spellRes = 0;

            foreach (KeyValuePair<string, int> spell in (rotation == "SpellPriorityAOE" ? SpellPriorityAOE : SpellPrioritySingle))
            {
                MethodInfo spellMethodName = type.GetMethod(spell.Key.Replace(" ", ""));
                //Utilities.log("Checking spell: " + spell.Key);

                spellRes = (int)spellMethodName.Invoke(null, null);
                if (spellRes > spellPrior)
                {
                    spellName = spell.Key;
                    spellPrior = spellRes;
                }
                if (spellPrior == 100) break;
            }
            //Utilities.log("Spell to use: " + spellName + " priority is :" + spellPrior.ToString());
            return spellName;
        }

        #endregion
        #endregion
    }
}
